#include "base/eventloop.h"
#include "base/log.h"
#include "net/connect.h"

#include <sys/time.h>
#include "net/heartbeat.h"
namespace net_tools::net
{

  Heartbeat::Heartbeat(net_tools::Eventloop *eventloop, connect_func heartbeat_lose_func)
      : eventloop_(eventloop),
        heartbeat_lose_func_(std::move(heartbeat_lose_func)),
        time_index_(1),
        free_time_index_(0)
  {
    uint64_t index = 1;
    for (int count = 0; count < 64; ++count)
    {
      index_vector_[count] = index;
      index = index << 1;
    }
    index_vector_[64] = 0x0000000000000000; //last one index_vector_[64] - 1 = 0x1111111111111111;
    for (int count = 0; count < _C::HEARTBEAT_VECTOR_SIZE; ++count)
    {
      heart_struct_[count] = new detail::heartbeat_struct();
    }
    Timeout();
  };
  void Heartbeat::Start()
  {
    timeval now_, sec_;
    gettimeofday(&now_, NULL);
    now_.tv_sec += 1;
    now_.tv_usec = 0;
    sec_.tv_sec = _C::HEARTBEAT_EVERY_SEC;
    sec_.tv_usec = _C::HEARTBEAT_EVERY_USEC;
    eventloop_->Run_every([this]
                          { Timeout(); },
                          now_, sec_, -1, "Heartbeat");
  };
  void Heartbeat::Stop()
  {
    eventloop_->Deltimejob_every("Heartbeat");
  };
  void Heartbeat::Timeout()
  {
    ++time_index_;
    free_time_index_ = time_index_ - 1; //always add in before
    if (time_index_ == _C::HEARTBEAT_VECTOR_SIZE)
      time_index_ = 0;
    detail::heartbeat_struct *now_heart_struct = heart_struct_[time_index_];
    uint32_t SIZE = now_heart_struct->all_min_heartbeat_vector_.size();
    for (uint_fast16_t count = 0; count < SIZE; ++count)
    {
      detail::min_heartbeat_struct *now_min_heart_struct = now_heart_struct->all_min_heartbeat_vector_[count];
      uint8_t connect_number = now_min_heart_struct->SIZE;
      // 00001111 | 11110000 = 11111111 ~11111111 = 00000000
      uint64_t LOSE = ~(now_min_heart_struct->IS_ALIVE | (~((index_vector_[connect_number]) - 1)));
      if (LOSE)
      {
        uint64_t lowbit;
        for (uint_fast8_t index = 0; LOSE != 0;)
        {
          lowbit = LOSE & (-LOSE);
          if (lowbit > 0x80)
          { //10000000;
            index += 8;
            LOSE = LOSE >> 8;
            continue;
          }
          switch (lowbit)
          {
          case 0x01:
            index += 1;
            LOSE >>= 1;
            break;
          case 0x02:
            index += 2;
            LOSE >>= 2;
            break;
          case 0x04:
            index += 3;
            LOSE >>= 3;
            break;
          case 0x08:
            index += 4;
            LOSE >>= 4;
            break;
          case 0x10:
            index += 5;
            LOSE >>= 5;
            break;
          case 0x20:
            index += 6;
            LOSE >>= 6;
            break;
          case 0x40:
            index += 7;
            LOSE >>= 7;
            break;
          case 0x80:
            index += 8;
            LOSE >>= 8;
            break;
          }
          index -= 1;
          heartbeat_lose_func_(now_min_heart_struct->connect_vector[index]); //will call Lose_heartbeat
          --connect_number;                                                  //now for last one index
          if (index < connect_number)
          {
            //last one move to lose connect index,only copy connect_vector;
            now_min_heart_struct->connect_vector[connect_number]->min_heartbeat_index_ = index;
            now_min_heart_struct->connect_vector[index] = now_min_heart_struct->connect_vector[connect_number];
          }
          index += 1;
          //over the last one,no need to move connect
        }                                 //for(uint_fast8_t index = 0;LOSE != 0;)
      }                                   //if(LOSE)
      now_min_heart_struct->IS_ALIVE = 0; //set 0,for next time call timeout
    }                                     //for(uint_fast16_t count = 0;count < SIZE;++count)
  };
  void Heartbeat::Add_heartbeat(Connect *connect)
  {
    if (connect->Is_add_heartbeat_)
    { // already in heartbeat
      return;
    }
    connect->heartbeat_ = this;
    connect->Is_add_heartbeat_ = true;
    detail::heartbeat_struct *heartbeat = heart_struct_[free_time_index_];
    detail::min_heartbeat_struct *free_heartbeat = *heartbeat->free_min_heartbeat_vector_.rbegin();
    connect->min_heartbeat_struct_ = free_heartbeat;
    connect->min_heartbeat_index_ = free_heartbeat->SIZE;
    free_heartbeat->connect_vector[free_heartbeat->SIZE] = connect;
    ++free_heartbeat->SIZE;
    if (free_heartbeat->SIZE == 64) [[unlikely]]
    {
      heartbeat->free_min_heartbeat_vector_.pop_back();
      if (!heartbeat->free_min_heartbeat_vector_.size())
      {
        heartbeat->No_Free_min_heartbeat_();
      }
    }
  };
  void Heartbeat::Del_heartbeat(Connect *connect)
  {
    if (!connect->Is_add_heartbeat_)
    {
      return;
    }
    connect->Is_add_heartbeat_ = false;
    detail::min_heartbeat_struct *min_heartbeat = connect->min_heartbeat_struct_;
    uint_fast8_t index = connect->min_heartbeat_index_;
    if (min_heartbeat->SIZE == 64) [[unlikely]]
    {
      connect->heartbeat_struct_->Add_free_min_heartbeat_(min_heartbeat);
    }
    --min_heartbeat->SIZE;
    if (!min_heartbeat->SIZE) [[unlikely]]
    {
      min_heartbeat->IS_ALIVE = 0;
      return;
    }
    // move IS_ALIVE, last one move to del_connect index
    uint64_t is_alive = min_heartbeat->IS_ALIVE;
    is_alive = (is_alive & (index_vector_[min_heartbeat->SIZE]));
    if (is_alive)
    {
      min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE | (index_vector_[index]);
      min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE & (~(index_vector_[min_heartbeat->SIZE]));
    }
    else
    {
      min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE & (~(index_vector_[index]));
    }
    min_heartbeat->connect_vector[min_heartbeat->SIZE]->min_heartbeat_index_ = index;
    min_heartbeat->connect_vector[index] = min_heartbeat->connect_vector[min_heartbeat->SIZE];
  };
  void Heartbeat::Lose_heartbeat(Connect *connect)
  {
    connect->Is_add_heartbeat_ = false;
    detail::min_heartbeat_struct *min_heartbeat = connect->min_heartbeat_struct_;
    if (min_heartbeat->SIZE == 64) [[unlikely]]
    {
      connect->heartbeat_struct_->Add_free_min_heartbeat_(min_heartbeat);
    }
    --min_heartbeat->SIZE;
  };

} // namespace net_tools::net